﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Sio.Mdm.BF.Ldap;
using Sio.Mdm.BF.Idm;
using Sio.Mdm.Types.Database;
using Sio.Mdm.Types;
using Sio.Mdm.Entities;
using Sio.Mdm.Client;

namespace Sio.Mdm.BL.Ldap
{
    /// <summary>
    /// This class provides DATA for ldap SYNC
    /// based on ldap rules
    /// </summary>
    class DataProvider
    {        
        Session _session = null;
        SyncType _syncType;
        LdapStreznik _ldapStreznik = null;                  // Trenutni
        LdapPravilo _ldapPravilo = null;                    // Trenutni
        LdapPredloga _ldapPredloga = null;                  // Trenutni
        SkupineProvider _skupineProvider = null;            // Relacija na skupine
        List<DataProviderPravilo> PravilaList = null;       // Nabor vseh pravil (ID-ji)
        EntityCollection<LdapSinObjekt> SinObjekti;         // Nabor vseh SIN OBJEKTOV (DATA)        
        int PravilaCursor = 0;                              // Trenutni index v PravilaList
        int SinObjektiCursor = 0;
        bool _pravilaIsLoaded = false;
        bool _dataIsLoaded = false;
        bool _autoLoadSkupine = true;
        EntityCollection _data;
        DataProviderPravilo _currentPravilo = null;

        public enum SyncType
        { 
            TimeInterval,           // Časovno proženje
            SyncRule,               // Proženje preko pravila
            UserRequest             // Proženje preko objekta
        }

        #region Enum
        private enum SyncObjectTypeID   // Enum v LDAP_SIN_OBJEKTI
        { 
            VseEntitete = 1,
            VsiUceci = 2,
            VsoOsebje = 3,
            VsiStarši = 4,
            Posamzenik = 5,
            Skupina = 6,
            Razred = 7
        }
        #endregion

        #region Properties
        #region Public
        public bool PravilaIsLoaded
        {
            get 
            {
                return _pravilaIsLoaded;
            }
        }
        public int PravilaCount
        {
            get {
                return PravilaList.Count();
            }
        }
        public int DataCount
        {
            get {
                return SinObjekti.Count();
            }
        }
        public bool DataIsLoaded
        {
            get {
                return _dataIsLoaded;
            }
        }
        public bool AutoLoadSkupine
        {
            get
            {
                return _autoLoadSkupine;
            }
            set {
                _autoLoadSkupine = value;
            }
        }
        public EntityCollection Data
        {
            get {
                return _data;
            }
        }
        public LdapPravilo ldapPravilo
        {
            get
            {
                if (_ldapPravilo == null)
                    _ldapPravilo = EntityFactory.CreateInstance<LdapPravilo>();
                return _ldapPravilo;
            }
            set
            {
                _ldapPravilo = value;
            }
        }
        public LdapStreznik ldapStreznik
        {
            get
            {
                if (_ldapStreznik == null)
                    _ldapStreznik = EntityFactory.CreateInstance<LdapStreznik>();
                return _ldapStreznik;
            }
            set
            {
                _ldapStreznik = value;
            }
        }
        public LdapPredloga ldapPredloga
        {
            get
            {
                if (_ldapPredloga == null)
                    _ldapPredloga = EntityFactory.CreateInstance<LdapPredloga>();
                return _ldapPredloga;
            }
            set
            {
                _ldapPredloga = value;
            }
        }
        #endregion
        
        #region Private
        #endregion

        #endregion

        // (1) PREPARATION

        #region Public
        public DataProvider(Session session)
        {
            _session = session;            
        }

        public Result LoadStreznik(int streznikId)
        {
            try
            {
                ldapStreznik = EntityFactory.LoadEntity<LdapStreznik>(new Filter<LdapStreznik>().Where(r => (r.LdapStreznikID == streznikId)));
            }
            catch (Exception ex) { 
                return new Result(ex);
            }

            return null;
        }

        /// <summary>
        /// Get rules (LDAP_PRAVILA) which must be executed for object.
        /// </summary>
        /// <param name="razredId"></param>
        /// <returns></returns>
        public Result PrepareDataUserRequestRazred(int razredId)
        {
            // Zaradi predpostavke/dogovora, da pravilo vedno vključuje le točno določen nabor/tip podatkov
            // je spodnji algoritem 'posebej poenostavljen'.

            _syncType = SyncType.UserRequest;
            _dataIsLoaded = false;
            _pravilaIsLoaded = false;

            try
            {
                // (1) Potrebno je dobiti OrgarnizacijaID na podlagi razreda...
                RazredSkupina razred = EntityFactory.LoadEntity<RazredSkupina>(new Filter<RazredSkupina>().Where(r => (r.RazredId == razredId)));
                int OrgarnizacijaID = razred.OrganizacijaId;

                // (2) Na podlagi razreda in znanega tipa lahko razberemo pravila...                                               
                PravilaList = new List<DataProviderPravilo>();

                EntityCollection<LdapSinObjekt> sinObjekti = new EntityCollection<LdapSinObjekt>(
                        from o in new Filter<LdapSinObjekt>()
                        join p in new Filter<LdapPravilo>() on o.LdapPraviloID equals p.LdapPraviloID                        
                        where       
				            p.Aktivno == true &&                                                        
                            o.ObjektTipID == (int)SyncObjectTypeID.VsiUceci &&
                            o.ObjektID == OrgarnizacijaID
                        select o
                    );
                
                
                
                foreach (LdapSinObjekt ldapSinObjekt in sinObjekti)
                {                    
                    DataProviderPravilo pravilo = new DataProviderPravilo(ldapSinObjekt.LdapPraviloID, razredId);
                    PravilaList.Add(pravilo);
                }                

                PravilaCursor = 0;
                return null;
            }
            catch (Exception ex)
            {
                return new Result(ex);
            }            
        }

        public Result PrepareDataUserRequestPravilo(int praviloId)
        {
            _dataIsLoaded = false;
            _pravilaIsLoaded = false;

            PravilaList = new List<DataProviderPravilo>();

            DataProviderPravilo pravilo = new DataProviderPravilo(praviloId);
            PravilaList.Add(pravilo);

            PravilaCursor = 0;
            return null;
        }

        public Result PravilaNext()
        {
            _pravilaIsLoaded = false;
            if (PravilaCursor > PravilaList.Count - 1) {
                return null;
            }
            
            Result result = PraviloLoad(PravilaList[PravilaCursor]);
            PravilaCursor++;

            return result;        
        }

        public Result DataNext()
        {
            _dataIsLoaded = false;
            if (SinObjektiCursor > SinObjekti.Count - 1)
            {
                return null;
            }

            Result result = DataLoad(SinObjekti[SinObjektiCursor]);
            SinObjektiCursor++;

            return result;
        }

        public List<string> GetClanSkupine(int idmStevilka)
        { 
            List<string> skupine;
            if (_skupineProvider == null || _skupineProvider.SkupineIsLoaded == false) {
                return null;
            }

            skupine = _skupineProvider.GetClanSkupine(ldapStreznik.LdapStreznikID, idmStevilka);
            return skupine;
        }
        #endregion

        #region Private
        private Result PraviloLoad(DataProviderPravilo pravilo)
        {
            _pravilaIsLoaded = false;
            _dataIsLoaded = false;
            _currentPravilo = pravilo;

            try 
            {                 
                ldapPravilo = EntityFactory.LoadEntity<LdapPravilo>(new Filter<LdapPravilo>().Where(r => (r.LdapPraviloID==pravilo.PraviloID)));
                if (ldapPravilo.Error != null || ldapPravilo.EntityState == EntityState.Unknown)
                {
                    return ldapPravilo.Error;
                }

                //(2) FOR PRAVILO LOAD SIN OBJECTS                                
                
                SinObjekti = new EntityCollection<LdapSinObjekt>(new Filter<LdapSinObjekt>().
                    Where(so => so.LdapPraviloID == pravilo.PraviloID));

                //(3) LOAD Support objects (only once per Pravilo)...

                ldapPredloga = EntityFactory.LoadEntity<LdapPredloga>(new Filter<LdapPredloga>().Where(r => (r.LdapPredlogaID == ldapPravilo.LdapPredlogaID)));                
                if (ldapPredloga.Error != null || ldapPredloga.EntityState == EntityState.Unknown) return ldapPredloga.Error;
                
                ldapStreznik = EntityFactory.LoadEntity<LdapStreznik>(new Filter<LdapStreznik>().Where(r => (r.LdapStreznikID == ldapPravilo.LdapStreznikID)));
                if (ldapStreznik.Error != null || ldapPredloga.EntityState == EntityState.Unknown) return ldapStreznik.Error;

                SinObjektiCursor = 0;
                _pravilaIsLoaded = true;

                //(4) LOAD Skupine (if necessary)...
                if (AutoLoadSkupine) {
                    if (_skupineProvider == null || _skupineProvider.SkupineIsLoaded == false || _skupineProvider.OrgarnizacijaID != ldapStreznik.OrgarnizacijaID)
                    {
                        _skupineProvider = new SkupineProvider(ldapStreznik.OrgarnizacijaID);
                        Result result = _skupineProvider.LoadSkupine();
                        if (result != null) return result;
                    }
                }

                return null;
            }
            catch (Exception ex)
            {
                return new Result(ex);
            }             
        }
        
        /// <summary>
        /// Here we actually load data that is passed to LdapService for SYNC
        /// </summary>
        /// <param name="sinObjekt"></param>
        /// <returns></returns>
        
        private Result DataLoad(LdapSinObjekt sinObjekt)
        {
            _dataIsLoaded = false;

            //(1) Load UČEČI for RazredID (first LetoID must be found...)
            if (sinObjekt.ObjektTipID==(int)SyncObjectTypeID.VsiUceci)
            {
                EntityCollection<Ucec> uceci = null;

                if (_currentPravilo.HasFilter && _currentPravilo.RazredID != null)
                {
                    uceci = new EntityCollection<Ucec>(
                        from r in new Filter<Ucec>()
                        join s in new Filter<UcecStatus>() on r.VlogaId equals s.VlogaId
                        join p in new Filter<RazredSkupina>() on s.RazredId equals p.RazredId
                        where
                            s.LetoId == s.LetoId &&
                            p.LetoId == s.LetoId &&
                            s.RazredId == _currentPravilo.RazredID
                        select r
                    );
                }
                else 
                {
                    uceci = new EntityCollection<Ucec>(
                        from r in new Filter<Ucec>()
                        join s in new Filter<UcecStatus>() on r.VlogaId equals s.VlogaId
                        join p in new Filter<RazredSkupina>() on s.RazredId equals p.RazredId
                        where
                            s.LetoId == s.LetoId &&
                            p.LetoId == s.LetoId &&
                            p.OrganizacijaId == ldapPravilo.OrgarnizacijaID
                        select r
                    );
                }
                
                _data = uceci;
                
                if (_data.Count > 0) _dataIsLoaded = true;
            }

            return null;
        }

        #endregion               

        #region DataProviderPravilo Class
        class DataProviderPravilo
        {
            int _praviloID=0;
            int? _razredID=null;
            bool _hasFilter = false;

            #region Properties
            public int PraviloID  
            {
                get 
                {
                    return _praviloID;
                }
            }
            public bool HasFilter 
            {
                get {
                    return _hasFilter;
                }
            }
            public int? RazredID  {
                get {
                    return _razredID;
                }
            }
            #endregion

            public DataProviderPravilo(int PraviloID)
            {
                _praviloID = PraviloID;
            }

            public DataProviderPravilo(int PraviloID, int RazredID)
            {
                _praviloID = PraviloID;
                _razredID = RazredID;
                _hasFilter = true;
            }
        }
        #endregion

        #region Backup
        public Result PrepareDataUserRequest_BCK(int razredId)
        {
            _syncType = SyncType.UserRequest;
            _dataIsLoaded = false;
            _pravilaIsLoaded = false;

            try
            {
                PravilaList = new List<DataProviderPravilo>();

                EntityCollection<LdapSinObjekt> sinObjekti = new EntityCollection<LdapSinObjekt>(
                    from so in new Filter<LdapSinObjekt>()
                    where so.ObjektID == razredId && so.ObjektTipID == (int)SyncObjectTypeID.Razred
                    select so
                );

                foreach (LdapSinObjekt ldapSinObjekt in sinObjekti)
                {
                    string t = ldapSinObjekt.LdapSinObjektID.ToString();

                    DataProviderPravilo pravilo = new DataProviderPravilo(ldapSinObjekt.LdapPraviloID, razredId);
                    PravilaList.Add(pravilo);
                }

                PravilaCursor = 0;
                return null;
            }
            catch (Exception ex)
            {
                return new Result(ex);
            }
        }
        #endregion
    }    
}
